using System.Collections.Generic;

namespace PropertyExpression.ReactiveRelation
{
    internal class ReverseDependencyManager<TCalcContext>
    {
        private readonly Dictionary<string, LinkedList<PropertyFormulaResolver>> dictionary =
            new Dictionary<string, LinkedList<PropertyFormulaResolver>>();

        public void Add(IFormulaProxy<TCalcContext> formulaProxy)
        {
            foreach (var cell in formulaProxy.GetValue().GetCells())
            {
                LinkedList<PropertyFormulaResolver> list;
                var key = GetKey(cell);
                if (!dictionary.TryGetValue(key, out list))
                {
                    list = new LinkedList<PropertyFormulaResolver>();
                    dictionary.Add(key, list);
                }
                list.AddLast(new PropertyFormulaResolver(formulaProxy, cell, this));
            }
        }

        public void SetIsCalculatedCells(
            object sender, 
            string propertyName, 
            CalculatedValueCache<TCalcContext> calculatedValueCache, 
            IPreviousValueProcessor previousValueProcessor, 
            CyclicFormulaResolver cyclicFormulaResolver)
        {
            LinkedList<PropertyFormulaResolver> list;
            if (dictionary.TryGetValue(
                GetKey(sender.GetType().FullName, propertyName), out list))
            {
                foreach (var propertyFormulaResolver in list)
                {
                    propertyFormulaResolver.CalculateProperty(
                        sender, calculatedValueCache, previousValueProcessor, cyclicFormulaResolver);
                }
            }
        }

        private static string GetKey(string entityFullName, string propertyName)
        {
            return string.Format("{0}|{1}", entityFullName, propertyName);
        }

        private static string GetKey(ICell cell)
        {
            return GetKey(cell.EntityFullName, cell.PropertyName);
        }

        private class PropertyFormulaResolver
        {
            private readonly IFormulaProxy<TCalcContext> formulaProxy;
            private readonly ICell cell;
            private readonly ReverseDependencyManager<TCalcContext> parent;

            public PropertyFormulaResolver(IFormulaProxy<TCalcContext> formulaProxy, ICell cell, 
                ReverseDependencyManager<TCalcContext> parent)
            {
                this.formulaProxy = formulaProxy;
                this.cell = cell;
                this.parent = parent;
            }

            public void CalculateProperty(
                object sender, 
                CalculatedValueCache<TCalcContext> calculatedValueCache, 
                IPreviousValueProcessor previousValueProcessor, 
                CyclicFormulaResolver cyclicFormulaResolver)
            {
                foreach (var reverseEntity in cell.GetReverseEntities(sender, previousValueProcessor))
                {
                    if (!cyclicFormulaResolver(reverseEntity, formulaProxy.PropertyName))
                    {
                        if (formulaProxy.SetValueToEntity(reverseEntity, calculatedValueCache))
                        {
                            parent.SetIsCalculatedCells(
                                reverseEntity, 
                                formulaProxy.PropertyName,
                                calculatedValueCache, 
                                previousValueProcessor, 
                                cyclicFormulaResolver);
                        }
                    }
                }
            }
        }
    }
}